//
//  SqlStatement.cpp
//  Phidgets
//
//  Created by Daniel Hazelbaker on 11/19/11.
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//

#include "SqlStatement.h"
#include "SystemDatabase.h"
#include "SqlValue.h"
#include <stdexcept>


//
// Create a new SqlStatement object in the given database.
//
SqlStatement::SqlStatement(SystemDatabase *database, sqlite3 *db, std::string sql, SqlParameters params)
{
    _database = database;
    if (sqlite3_prepare_v2(db, sql.c_str(), -1, &_statement, NULL) != SQLITE_OK)
        throw std::runtime_error("Invalid SQL statement");
    
    _parameters = params;
    BindParameters();
}


//
// Finalize the statement and release memory.
//
SqlStatement::~SqlStatement()
{
    sqlite3_finalize(_statement);
    _statement = NULL;
}


//
// Bind all parameters for this query.
//
void SqlStatement::BindParameters()
{
    SqlParameters::iterator it;
    int index;
    
    
    sqlite3_reset(_statement);
    for (it = _parameters.begin(); it != _parameters.end(); it++)
    {
        index = sqlite3_bind_parameter_index(_statement, (*it).first.c_str());
        if (index > 0)
            (*it).second.bindValue(_statement, index);
    }
}


//
// Execute and return a true/false indicating if this query was successful.
//
bool SqlStatement::ExecuteNonQuery()
{
    bool status;
    
    
    _database->Lock();
    status = (sqlite3_step(_statement) == SQLITE_DONE);
    _lastInsertID = _database->LastInsertRowID();
    _database->Unlock();
    
    return status;
}


//
// Execute the query and return the first column/row of the result.
//
SqlValue SqlStatement::ExecuteScalar()
{
    if (NextRow() == false)
        return SqlValue();
    
    return (*_values.begin()).second;
}


//
// Advance the query to the next row of data and return true if that row is available.
//
bool SqlStatement::NextRow()
{
    int result;
    bool status;
    
    
    _database->Lock();
    result = sqlite3_step(_statement);
    status = (result == SQLITE_ROW);
    _values = SqlValues();
    if (status)
    {
        for (int i = 0; i < sqlite3_column_count(_statement); i++)
        {
            const char *name = sqlite3_column_name(_statement, i);
            
            switch (sqlite3_column_type(_statement, i))
            {
                case SQLITE_INTEGER:
                    _values[name] = SqlValue(sqlite3_column_int(_statement, i));
                    break;
                    
                case SQLITE_TEXT:
                    _values[name] = SqlValue((const char *)sqlite3_column_text(_statement, i));
                    break;
                    
                case SQLITE_FLOAT:
                case SQLITE_BLOB:
                case SQLITE_NULL:
                    _values[name] = SqlValue();
                    break;
            }
        }
    }
    _database->Unlock();
    
    return status;
}


